package com.quan.base.common.utils.oss.qiniu.client;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.BatchStatus;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.storage.model.FileListing;
import com.qiniu.util.Auth;
import com.quan.base.common.utils.EncryptPropertyConfigurer;
import com.quan.base.common.utils.oss.common.ResponseMsg;
import com.quan.base.common.utils.oss.common.qiniu.DomainHolder;
import com.quan.base.common.utils.oss.common.qiniu.FilePage;
import com.quan.base.common.utils.oss.common.qiniu.ResponseInfo;
import com.quan.base.common.utils.oss.common.tools.GeneratorName;

/**
 * 七牛云 OSS 工具类
 * 
 * @author yhaoquan
 * 
 */
@Component
public class QiniuOSSClient {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	// AccessKey 访问Key
	private String accessKey = EncryptPropertyConfigurer.getValue("qiniu.AccessKey");

	// SecretKey 密钥
	private String secretKey = EncryptPropertyConfigurer.getValue("qiniu.SecretKey");

	/**
	 * 上传文件
	 * @param file 文件对象
	 * @param newName 文件名称（不需要后缀）
	 * @param dir 目录：weixin/imgage/avatar（目录不存在，自动创建）
	 * @return
	 */
	public ResponseMsg upload(String bucketName, File file, String prefixName) {
		String extension = FilenameUtils.getExtension(file.getName());
		try {
			
			String key = GeneratorName.fileName(file.getName(), prefixName) ;
			
			Response response = getUploadManager().put(file, key, getUploadToken(bucketName));
			
			DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);

			if (response.statusCode >= 200 && response.statusCode <= 300) {
				ResponseMsg responseMsg = new ResponseMsg(true, response.statusCode, "成功", bucketName, key, null, extension, getDownloadUrl(bucketName, putRet.key)) ;
				logger.info("上传文件[七牛云]：{}", responseMsg.toString());
				return responseMsg ;
			} else {
				ResponseMsg responseMsg = new ResponseMsg(false, response.statusCode, "失败", bucketName, key, null, extension, getDownloadUrl(bucketName, putRet.key)) ;
				logger.info("上传文件[七牛云]：{}", responseMsg.toString());
				return responseMsg ;
			}
		} catch (QiniuException e) {
			Response response = e.response;
			ResponseMsg responseMsg = new ResponseMsg(false, response.statusCode, "异常", bucketName, file.getName(), null, extension, null) ;
			logger.info("上传文件[七牛云]==》{}", responseMsg.toString());
		}
		return null;
	}
	
	/**
	 * 上传文件，默认使用UUID为文件名称
	 * @param file 文件对象
	 * @return
	 */
	public ResponseMsg upload(String bucketName, File file) {
		return this.upload(bucketName, file, null);
	}
	
	/**
	 * 上传文件
	 * @param bucketName 空间名称
	 * @param byteArray 文件字节数组
	 * @param fileName 原始文件名称，包括后缀
	 * @param dir 目录：weixin/imgage/avatar（目录不存在，自动创建）
	 * @return
	 */
	public ResponseMsg upload(String bucketName, byte[] byteArray, String originalFilename, String prefixName) {
		String extension = FilenameUtils.getExtension(originalFilename);
		try {
			
			String key = GeneratorName.fileName(originalFilename, prefixName) ;
			
			Response response = getUploadManager().put(byteArray, key, getUploadToken(bucketName)) ;
			
			DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);

			if (response.statusCode >= 200 && response.statusCode <= 300) {
				ResponseMsg responseMsg = new ResponseMsg(true, response.statusCode, "成功", bucketName, key, null, extension, getDownloadUrl(bucketName, putRet.key)) ;
				logger.info("上传文件[七牛云]：{}", responseMsg.toString());
				
				return responseMsg ;
			} else {
				ResponseMsg responseMsg = new ResponseMsg(false, response.statusCode, "失败", bucketName, key, null, extension, getDownloadUrl(bucketName, putRet.key)) ;
				logger.info("上传文件[七牛云]：{}", responseMsg.toString());
				return responseMsg ;
			}
		} catch (QiniuException e) {
			Response response = e.response;
			ResponseMsg responseMsg = new ResponseMsg(false, response.statusCode, "异常", bucketName, originalFilename, null, extension, null) ;
			logger.info("上传文件[七牛云]：{}", responseMsg.toString());
		}
		return null;
	}
	public ResponseMsg upload(String bucketName, byte[] byteArray, String fileName) {
		return this.upload(bucketName, byteArray, null, null) ;
	}
	public ResponseMsg upload(String bucketName, byte[] byteArray) {
		return this.upload(bucketName, byteArray, null, null) ;
	}
	
	/**
	 * 删除文件
	 * @param bucketName 存储空间
	 * @param key 文件名称
	 * @return
	 */
	public ResponseInfo delete(String bucketName, String key) {
		BucketManager bucketManager = new BucketManager(getAuth(), getConfiguration());
		try {
			Response response = bucketManager.delete(bucketName, key) ;
			
			ResponseInfo responseInfo = new ResponseInfo("成功", true, response.statusCode, bucketName, key);
			logger.info("删除文件[七牛云]：{}", responseInfo.toString());
			return responseInfo ;
		} catch (QiniuException e) {
			Response response = e.response;
			ResponseInfo responseInfo = new ResponseInfo("异常", true, response.statusCode, bucketName, key);
			logger.error("删除文件[七牛云]==》{}", responseInfo.toString());
		}
		return null;
	}
	
	/**
	 * 获取空间下的文件列表
	 * @param filePage
	 * @return
	 */
	public FilePage fileList(FilePage filePage) {
		BucketManager bucketManager = new BucketManager(getAuth(), getConfiguration());
		
		try {
			FileListing listFiles = bucketManager.listFiles(filePage.getBucketName(), filePage.getPrefix(), filePage.getMarker(), filePage.getLimit(), filePage.getDelimiter()) ;
			
			List<FilePage> list = new ArrayList<FilePage>() ;
			
			//上一次列举返回的位置标记，作为本次列举的起点信息
			filePage.setMarker(listFiles.marker);
			
			FileInfo[] items = listFiles.items;
			for (FileInfo fileInfo : items) {
				FilePage files = new FilePage() ;
				files.setKey(fileInfo.key);
				files.setFsize(fileInfo.fsize);
				files.setPutTime(fileInfo.putTime);
				files.setMimeType(fileInfo.mimeType);
				files.setHash(fileInfo.hash);
				files.setDownloadUrl(getDownloadUrl(filePage.getBucketName(), fileInfo.key));
				
				list.add(files) ;
			}
			
			filePage.setFileList(list);
			filePage.setStatus(true);
			
			logger.info("获取文件列表[七牛云]==》空间名称：{}", filePage.getBucketName());
		} catch (QiniuException e) {
			Response response = e.response;
			filePage.setErrorMsg(response.error);
			filePage.setStatus(false);
			logger.error("获取文件列表[七牛云]==》空间名称：{}, 错误信息：{}", filePage.getBucketName(), response.error);
		}
		return filePage ;
	}
	
	/**
	 * 批量删除文件
	 * @param bucketName 空间名称
	 * @param keyList 文件数组
	 */
	public void deleteBatch(String bucketName, String[] keyList) {
		BucketManager bucketManager = new BucketManager(getAuth(), getConfiguration());
		
		try {
			BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
			batchOperations.addDeleteOp(bucketName, keyList);
			Response response = bucketManager.batch(batchOperations);
			BatchStatus[] batchStatusList = response.jsonToObject(BatchStatus[].class);
			for (int i = 0; i < keyList.length; i++) {
			    BatchStatus status = batchStatusList[i];
			    String key = keyList[i];
			    if (status.code == 200) {
			    	logger.info("批量删除文件[七牛云]==》状态：{}, 空间名称：{}, 文件名称：{}", "成功", bucketName, key);
			    } else {
			        logger.info("批量删除文件[七牛云]==》状态：{}, 空间名称：{}, 文件名称：{}, 错误信息：{}", "失败", bucketName, key, status.data.error);
			    }
			}
		} catch (QiniuException e) {
			Response response = e.response;
			logger.error("批量删除文件[七牛云]==》状态：{}, 空间名称：{}, 文件名称：{}", "失败", bucketName, response.error);
			e.printStackTrace();
		}
	}
	/**
	 * 批量删除文件
	 * @param bucket 空间名称
	 * @param keyList 多个文件“，”号分割
	 */
	public void deleteBatch(String bucket, String keyList) {
		if(StringUtils.isNotBlank(keyList)) {
			this.deleteBatch(bucket, keyList.split(","));
		}
		
	}
	
	/**
	 * 获取文件下载URL
	 * @param key
	 * @return
	 */
	public String getDownloadUrl(String bucketName, String key) {
		try {
			return "http://" + String.format("%s/%s", getBuckeDomain(bucketName), URLEncoder.encode(key, "utf-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 获取 Bucket 列表
	 * @return
	 */
	public List<String> getBuckets() {
		String response = this.http("http://rs.qbox.me/buckets") ;
		try {
			List<String> buckets = new ArrayList<String>() ;
			JSONArray parseArray = JSON.parseArray(response);
			for (int i=0; i<parseArray.size(); i++) {
				buckets.add(parseArray.getString(i)) ;
			}
			return buckets ;
		} catch (Exception e) {
			JSONObject parseObject = JSON.parseObject(response) ;
			logger.error("获取Buckets[七牛云]==》错误信息：{}", parseObject.get("error"));
		}
		return null;
	}
	
	public String getBuckeDomain(String bucketName) {
		Map<String, Object> domainMap = DomainHolder.getDomain() ;
		
		if(null != domainMap && null != domainMap.get(bucketName)) { //从缓存中获取
			return domainMap.get(bucketName).toString() ;
		} else {
			if(null == domainMap) {
				domainMap = new HashMap<String, Object>() ;
			}
			//远程获取空间域名
			String remoteBucketDomain = getRemoteBucketDomain(bucketName) ;
			
			//保存到缓存
			domainMap.put(bucketName, remoteBucketDomain) ;
			DomainHolder.setDomain(domainMap);
			
			return remoteBucketDomain ;
		}
	}
	
	public String getRemoteBucketDomain(String bucketName) {
		String response = this.http("http://api.qiniu.com/v6/domain/list?tbl="+bucketName) ;
		try {
			JSONArray parseArray = JSON.parseArray(response);
			return parseArray.getString(0) ;
		} catch (Exception e) {
			JSONObject parseObject = JSON.parseObject(response) ;
			logger.error("获取 Bucket 空间域名[七牛云]==》错误信息：{}", parseObject.get("error"));
		}
		return null;
	}
	
	/**
	 * 获取文件上对象
	 * 
	 * @return
	 */
	public UploadManager getUploadManager() {
		UploadManager uploadManager = new UploadManager(getConfiguration());
		return uploadManager;
	}

	/**
	 * 设置文件存储区域 
	 * 华东 z0 服务器端上传：http(s)://up.qiniup.com 客户端上传： http(s)://upload.qiniup.com 
	 * 华北 z1 服务器端上传：http(s)://up-z1.qiniup.com 客户端上传： http(s)://upload-z1.qiniup.com 
	 * 华南 z2 服务器端上传：http(s)://up-z2.qiniup.com 客户端上传： http(s)://upload-z2.qiniup.com 
	 * 北美 z3 服务器端上传：http(s)://up-na0.qiniup.com 客户端上传：http(s)://upload-na0.qiniup.com
	 * 
	 * @return
	 */
	public Configuration getConfiguration() {
		return new Configuration(Zone.zone2());
	}

	/**
	 * 获取上传文件的凭证
	 * 
	 * @return
	 */
	public String getUploadToken(String bucketName) {
		String uploadToken = getAuth().uploadToken(bucketName);
		return uploadToken;
	}
	
	/**
	 * 验证密钥
	 * 
	 * @return
	 */
	public Auth getAuth() {
		Auth auth = Auth.create(this.accessKey, this.secretKey);
		return auth;
	}
	
	public String http(String url) {
		OkHttpClient okHttpClient = new OkHttpClient() ;
		
		String authorization = (String) getAuth().authorization(url).get("Authorization") ;
		
		Builder builder = new Request.Builder().url(url);
		builder.addHeader("Content-Type", "application/x-www-form-urlencoded");	
		builder.addHeader("Authorization", authorization);
		Request request = builder.build() ;
		
		Call call = okHttpClient.newCall(request);
		try {
			okhttp3.Response response = call.execute() ;
			
			return response.body().string() ;
		} catch (IOException e) {
			logger.error("七牛云操作  API 异常==》错误信息：{}", e.getMessage());
		}
		return null;
	}
	
	/**
	 * 对操作的API参数进行编码
	 * @param url 操作的URL API
	 * @param params 参数，需编码
	 * @param encode 是否编码
	 * @return
	 */
	public String http(String url, Map<String, Object> params, boolean encode) {
		OkHttpClient okHttpClient = new OkHttpClient() ;
		
		//对API编码
		if(encode) {
			//UrlSafeBase64.encodeToString(api);
		}
		String authorization = (String) getAuth().authorization(url).get("Authorization") ;
		
		Builder builder = new Request.Builder().url(url);
		builder.addHeader("Content-Type", "application/x-www-form-urlencoded");	
		builder.addHeader("Authorization", authorization);
		Request request = builder.build() ;
		
		Call call = okHttpClient.newCall(request);
		try {
			okhttp3.Response response = call.execute() ;
			
			return response.body().string() ;
		} catch (IOException e) {
			logger.error("七牛云操作  API 异常==》错误信息：{}", e.getMessage());
		}
		return null;
	}
	
}
